#pragma once
#include <iostream>
#include <sqlite3.h>
#include <vector>
#include <string>
#include <sstream>
#include <random>
#include <iomanip>
#include <atomic>
#include <fstream>
#include <cstring>
#include <sys/stat.h>

#include "Log.hpp"

namespace HelperModule
{
    using namespace LogModule;
    /*
    SqliteHelper
    1. 创建/打开对应的数据库文件
    2. 针对打开的数据库文件进行操作
        [1] 表操作
        [2] 数据操作
    3. 关闭数据库文件
    */
    class SqliteHelper
    {
    public:
        typedef int (*SqliteCallback)(void *, int, char **, char **); // 回调函数，用于决定如何处理得到数据的函数

        SqliteHelper(const std::string &dbfile)
            : _dbfile(dbfile),
              _handler(nullptr)
        {
        }

        bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
        {
            /*
            flag:
                SQLITE_OPEN_READWRITE -- 以可读可写方式打开数据库文件
                SQLITE_OPEN_CREATE -- 不存在数据库文件则创建
                SQLITE_OPEN_NOMUTEX--多线程模式，只要不同的线程使用不同的连接即可保证线程安全
                SQLITE_OPEN_FULLMUTEX--串行化模式
            返回： SQLITE_OK 表示成功
            */
            // int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_level, nullptr);
            if (ret != SQLITE_OK)
            {
                LogModule::LOG(LogLevel::ERROR) << "打开/创建sqlite数据库失败: " << sqlite3_errmsg(_handler);
                return false;
            }
            return true;
        }

        bool exec(const std::string &sql, SqliteCallback cb, void *arg)
        {
            /*
            int sqlite3_exec(sqlite3*, char *sql, int(*callback)(void*,int,char**,char**), void* arg, char **err)
                int (*callback)(void*,int,char**,char**)
                void* : 是设置的在回调时传入的 arg 参数
                int：一行中数据的列数
                char**：存储一行数据的字符指针数组
                char**：每一列的字段名称
                这个回调函数有个 int 返回值，成功处理的情况下必须返回 0，返回非 0
                会触发 ABORT 退出程序
                返回： SQLITE_OK 表示成功
            */
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                LogModule::LOG(LogLevel::ERROR) << sql << " 执行语句失败: " << sqlite3_errmsg(_handler);
                return false;
            }
            return true;
        }

        bool close()
        {
            /*
                int sqlite3_close_v2(sqlite3*); 推荐使用--无论如何都会返回SQLITE_OK
            */
            if (_handler)
            {
                sqlite3_close_v2(_handler);
                return true;
            }
            return false;
        }

    private:
        std::string _dbfile; /* 数据库文件路径 */
        sqlite3 *_handler;   /* 操作句柄 */
    };

/*---------------------------------------------------------------------------------------------*/

    // news.music.#.pop    sep = '.'   vector<std::string> result
    // news music # pop
    class SplitHelper
    {
    public:
        static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &result)
        {
            int pos = 0, index = 0;
            while (index < str.size())
            {
                pos = str.find(sep, index);
                // 1. 说明找到结尾也没有
                if (pos == str.npos)
                {
                    result.emplace_back(str.substr(index));
                    return result.size();
                }
                // 2. 如果存在 news...music.#.pop  sep在一起的情况
                if (pos == index)
                {
                    index = pos + sep.size();
                    continue;
                }
                // 3. 找到'.' ,将'.'前面的字符串保存起来
                result.emplace_back(str.substr(index, pos - index));
                index = pos + sep.size();
            }
            return result.size();
        }
    };

/*---------------------------------------------------------------------------------------------*/

    class UUIDHelper
    {
    public:
        // UUID组成
        // 0~255的随机数字转化为8个16进制的数, 按8/4/4分割, 分割符为'-'
        // 16位16进制以1递增的代码, 按4/12分割, 分割符为'-'
        // 例如：f986bb99-2fac-0db6-0000-000000000001
        static std::string GetUUID()
        {
            // 1. 通过random_device生个一个随机种子，给伪随机生成mt19937_64提供一个种子
            std::random_device rd;
            std::mt19937_64 gernator(rd());
            // 2. 使用uniform_int_distribution规定生产随机数的范围0~255
            std::uniform_int_distribution<int> distribution(0, 255);
            // 3. 生成8个随机数，16进制，并且以8/4/4分割，2位位宽，以0补位
            std::stringstream ss;
            for (int i = 0; i < 8; ++i)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(gernator);
                if (i == 3 || i == 5 || i == 7)
                    ss << "-";
            }

            // 4. 生产16位16进制以1递增的代码, 按4/12分割, 分割符为'-'
            // 0xFFFF 1111 1111 1111 1111
            // &      0000 0000 0000 0001
            static std::atomic<size_t> seq(1);
            size_t num = seq.fetch_add(1);
            for (int i = 7; i >= 0; --i)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (i * 8)) & 0xFF);
                if (i == 6)
                    ss << "-";
            }

            return ss.str();
        }
    };

/*---------------------------------------------------------------------------------------------*/

    /*
    实现一个文件类,功能如下
    1. 文件是否存在
    2. 读取/写入文件
    3. 获取指定文件的目录
    4. 创建/删除文件
    5. 创建/删除目录
    */
    class FileHelper
    {
    public:
        FileHelper(const std::string &filename)
            : _filename(filename)
        {
        }

        bool Exits()
        {
            struct stat st;
            return (stat(_filename.c_str(), &st) == 0);
        }
        size_t size()
        {
            struct stat st;
            int ret = stat(_filename.c_str(), &st);
            if (ret < 0)
            {
                // 文件不存在
                return 0;
            }
            return st.st_size;
        }

        bool Read(char *body, size_t offset, size_t len)
        {
            // 1. 打开文件
            std::ifstream ifs(_filename, std::ios::binary | std::ios::in);
            if (ifs.is_open() == false)
            {
                LogModule::LOG(LogLevel::ERROR) << "读取打开文件失败：" << _filename;
                return false;
            }
            // 2. 跳转文件指定位置
            ifs.seekg(offset, std::ios::beg);
            // 3. 读取文件数据
            ifs.read(body, len);
            if(ifs.good() == false)
            {
                LogModule::LOG(LogLevel::ERROR) << "读取文件失败：" << _filename;
                // 关闭文件
                ifs.close();
                return false;
            }
            // 4. 关闭文件
            ifs.close();
            return true;
        }

        bool Read(std::string &body)
        {
            // 获取文件整体的大小
            size_t fsize = this->size();
            // 将body调整到指定大小
            body.resize(fsize);
            return Read(&body[0], 0, fsize); // 使用&body[0]是因为body.c_str() --> const char*
        }

        bool Write(const char *body, size_t offset, size_t len)
        {
            // 1. 打开文件
            // 由于存在跳转文件指定位置这部操作,需要有读操作，因此使用fstream
            std::fstream fs(_filename, std::ios::binary | std::ios::in | std::ios::out);
            if (fs.is_open() == false)
            {
                LogModule::LOG(LogLevel::ERROR) << "写入打开文件失败：" << _filename;
                return false;
            }

            // 2. 跳转到文件指定位置
            fs.seekp(offset, std::ios::beg);
            // 3. 写入数据
            fs.write(body, len);
            if(fs.good() == false)
            {
                LogModule::LOG(LogLevel::ERROR) << "写入文件失败：" << _filename;
                fs.close();
                return false;
            }
            // 4. 关闭文件
            fs.close();
            return true;
        }


        bool Write(const std::string &body)
        {
            return Write(body.c_str(), 0, body.size());
        }

        bool rename(const std::string &nname) 
        {
            return (::rename(_filename.c_str(), nname.c_str()) == 0);
        }

        static std::string parentDirectory(const std::string &filename)
        {
            // aaa/bbb/ccc/data.txt --> 获取 aaa/bbb/ccc
            // 从后往前找到第一个'/'
            size_t pos = filename.find_last_of("/");
            if(pos == std::string::npos)
            {
                // data.txt
                return "./";
            }
            std::string path = filename.substr(0, pos);
            return path;
        }

        // 创建/删除文件
        static bool Createfile(const std::string &filename)
        {
            std::fstream ofs(filename, std::ios::binary | std::ios::out);
            if (ofs.is_open() == false)
            {
                LogModule::LOG(LogLevel::ERROR) << "创建文件失败：" << filename;
                return false;
            }
            ofs.close();
            return true;
        }

        static bool Destoryfile(const std::string &filename)
        {
            return (::remove(filename.c_str()) == 0);
        }

        // 创建/删除目录
        static bool Creatediretory(const std::string &path)
        {
            // a/b/c
            int pos = 0, index = 0;
            while(index < path.size())
            {
                pos = path.find("/", index);
                if(pos == std::string::npos)
                {
                    // /c
                    return (mkdir(path.c_str(), 0775) == 0);
                }
                std::string subpath = path.substr(0, pos);
                int ret = mkdir(subpath.c_str(), 0775);
                if(ret < 0 && errno != EEXIST)
                {
                    LogModule::LOG(LogLevel::ERROR) << "创建目录失败：" << subpath << " " << strerror(errno);
                    return false;
                }
                index = pos + 1;
            }
            return true;
        }

        static bool Destorydiretory(const std::string &path)
        {
            // 使用系统调用进行删除
            // rm -rf path
            std::string command = "rm -rf " + path;
            return (system(command.c_str()) != 0);
        }

    private:
        std::string _filename;
    };
}